package com.witsight.hibernate.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.witsight.platform.util.lang.StringUtil;
/**   
 * 说明：sql模板处理器
 * @Title: TextTemplates.java 
 * @Package com.witsight.hibernate.util 
 * @See: {@link TextTemplates}
 * Copyright: Copyright (c) 2017
 * Company:sany huax witsight team by product
 * @author: fuyp  
 * @date: 2017年9月14日 下午3:54:21 
 * @version: V1.0
 *
 */
public final class TextTemplates {
	/**
     * Logger
     */
    private static final Logger log = LoggerFactory.getLogger(TextTemplates.class);

    /**
     * Pattern: Template
     */
    private static final Pattern PATTERN_TEMPLATE = Pattern
            .compile("\\{\\$(\\w+)\\}");

    private static final Pattern PATTERN_OPT_TEMPLATE = Pattern.compile("\\{(\\w+)\\}");

    /**
     * Pattern: Optional fragments
     */
    private static final Pattern PATTERN_OPTIONAL = Pattern.compile("\\[[^\\[\\]]+\\]");

    /**
     * Pattern: Tag names
     */
    private static final Pattern PATTERN_OPT_TAG = Pattern.compile(":(\\w+)");

    /**
     * Empty parameter
     */
    private static final Map<String, Object> EMPTY_PARAM = new HashMap<String, Object>(1);

    /**
     * Prevents instantiation.
     */
    private TextTemplates() {
    }

    public static String replaceTemplate(String tplTxt, Map<String, ?> param,
                                         boolean replaceWithEmpty) {
        return replaceTemplate(tplTxt, PATTERN_TEMPLATE, param, replaceWithEmpty);
    }

    /**
     * Replaces the '{$xxx}'-like templates with the given parameters. The tag
     * names (xxx) must be composed of word characters ([a-zA-Z_0-9]) and
     * case-sensitive. If the value for a certain tag is found and not null, the
     * caught tag will be replaced with the value's text representation;
     * otherwise the replacement will be an empty string if the given
     * <code>replaceWithEmpty</code> is true or the original '{$xxx}'-like
     * text if given false.
     *
     * @param tplTxt           the original template text
     * @param param            parameters
     * @param replaceWithEmpty if <code>true</code>, the '{$xxx}' will be replaced with an
     *                         empty text if the value of xxx does not exist;<br>
     *                         if <code>false</code>, the '{$xxx}' will be left unchanged
     *                         if its value does not exist
     * @return the new text with all '{$xxx}'-like templates replaced
     */
    public static String replaceTemplate(String tplTxt, Pattern pattern, Map<String, ?> param,
                                         boolean replaceWithEmpty) {
        if (log.isDebugEnabled()) {
            log.debug("Tpl. Text=" + tplTxt + ", Param=" + param
                    + ", ReplaceWithEmpty" + replaceWithEmpty);
        }

        if (param == null) {
            param = EMPTY_PARAM;
        }

        String ret = tplTxt;
        Matcher matcher = pattern.matcher(tplTxt);
        if (matcher.find()) {
            // At least one template tag has been found.
            StringBuffer buff = new StringBuffer(tplTxt.length());

            do {
                // Name of the tag caught here
                String tagName = matcher.group(1);
                // Value set in the parameters for the tag and its text
                // replacement
                Object tagValue = param.get(tagName);
                // Tag Section
                String tagSection = matcher.group();
                if (tagValue == null) {
                    matcher.appendReplacement(buff, "");
                    if (!replaceWithEmpty) {
                        buff.append(tagSection);
                    }
                } else {
                    matcher.appendReplacement(buff, String.valueOf(tagValue));
                }
            } while (matcher.find());

            matcher.appendTail(buff);
            ret = buff.toString();
        }

        if (log.isDebugEnabled()) {
            log.debug("Return=" + ret);
        }
        return ret;
    }

    /**
     * Replaces the '{$xxx}'-like templates with the given parameters. The tag
     * names (xxx) must be composed of word characters ([a-zA-Z_0-9]) and
     * case-sensitive. If the value for a certain tag is found and not null, the
     * caught tag will be replaced with the value's text representation;
     * otherwise the replacement will be an empty string.
     *
     * @param tplTxt the original template text
     * @param param  parameters
     * @return the new text with all '{$xxx}'-like templates replaced
     * @see #replaceTemplate(String, java.util.Map, boolean)
     */
    public static String replaceTemplate(String tplTxt, Map<String, ?> param) {
        return replaceTemplate(tplTxt, param, true);
    }

    /**
     * Removes the optional fragment (surrounded with '[]') in the given
     * template text if the specified condition is matched.
     *
     * @param tplTxt      the original template text
     * @param param       parameters
     * @param findAll     if <code>true</code>, the fragment will be removed unless
     *                    all tag values inside are not null;<br>
     *                    if <code>false</code>, the fragment will be retained unless
     *                    all of the tag values inside are null.
     * @param ignoreNoTag if <code>true</code>, the fragment without any tag will be
     *                    reserved;<br>
     *                    if <code>false</code>, the fragment will be removed
     * @return the text with all unnecessary optinal fragment removed
     */
    public static String removeOptionalFrag(String tplTxt, Map<String, ?> param,
                                            boolean findAll, boolean ignoreNoTag) {
        if (log.isDebugEnabled()) {
            log.debug("Tpl. Text=" + tplTxt + ", Param=" + param + ", FindAll="
                    + findAll + ", IgnoreNoTag" + ignoreNoTag);
        }

        if (param == null) {
            param = EMPTY_PARAM;
        }

        String ret = tplTxt;
        Matcher optMatcher = PATTERN_OPTIONAL.matcher(tplTxt);
        if (optMatcher.find()) {
            // At least one optional fragment has been found.
            StringBuffer buff = new StringBuffer(tplTxt.length());

            outer:
            do {
                // Optional fragment
                String fragment = optMatcher.group();
                // Frag. matcher
                Matcher fragMatcher = PATTERN_OPT_TAG.matcher(fragment);
                if (fragMatcher.find()) {
                    // Tag count
                    int count = 0;
                    int found = 0;
                    do {
                        // Name of the tag caught here
                        String tagName = fragMatcher.group(1);
                        // Value set in the parameters for the tag and its text
                        // replacement
                        Object tagValue = param.get(tagName);
                        if (tagValue == null
                                || StringUtil.isEmpty(tagValue.toString())) {
                            if (findAll) {
                                optMatcher.appendReplacement(buff, "");
                                continue outer;
                            }
                        } else {
                            if (!findAll) {
                                optMatcher.appendReplacement(buff, fragment);
                                continue outer;
                            }

                            found++;
                        }
                        count++;
                    } while (fragMatcher.find());

                    if ((findAll && count == found) || (!findAll && found != 0)) {
                        optMatcher.appendReplacement(buff, fragment.substring(
                                1, fragment.length() - 1));
                    } else {
                        optMatcher.appendReplacement(buff, "");
                    }
                } else {
                    // Tag not found
                    if (ignoreNoTag) {
                        optMatcher.appendReplacement(buff, fragment);
                    } else {
                        optMatcher.appendReplacement(buff, fragment.substring(
                                1, fragment.length() - 1));
                    }
                }
            } while (optMatcher.find());

            optMatcher.appendTail(buff);
            ret = buff.toString();
        }

        optMatcher = PATTERN_OPTIONAL.matcher(ret);

        // Replace template tag
        if (optMatcher.find()) {
            // At least one optional fragment has been found.
            StringBuffer buff = new StringBuffer(ret.length());
            outer:
            do {
                // Optional fragment
                String fragment = optMatcher.group();
                // Frag. matcher
                Matcher fragMatcher = PATTERN_OPT_TEMPLATE.matcher(fragment);
                if (fragMatcher.find()) {
                    // Tag count
                    int count = 0;
                    int found = 0;
                    do {
                        // Name of the tag caught here
                        String tagName = fragMatcher.group(1);
                        // Value set in the parameters for the tag and its text
                        // replacement
                        Object tagValue = param.get(tagName);
                        if (tagValue == null
                                || StringUtil.isEmpty(tagValue.toString())) {
                            if (findAll) {
                                optMatcher.appendReplacement(buff, "");
                                continue outer;
                            }
                        } else {
                            String s = fragment.substring(1, fragMatcher.start()) + String.valueOf(tagValue) + fragment.substring(fragMatcher.end(), fragment.length() - 1);
                            optMatcher.appendReplacement(buff, s);
                            if (!findAll) {
                                continue outer;
                            }

                            found++;
                        }
                        count++;
                    } while (fragMatcher.find());

                    if ((findAll && count == found) || (!findAll && found != 0)) {
//						optMatcher.appendReplacement(buff, fragment.substring(
//								1, fragment.length() - 1));
                    } else {
                        optMatcher.appendReplacement(buff, "");
                    }
                } else {
                    // Tag not found
                    if (ignoreNoTag) {
                        optMatcher.appendReplacement(buff, fragment);
                    } else {
                        optMatcher.appendReplacement(buff, fragment.substring(
                                1, fragment.length() - 1));
                    }
                }
            } while (optMatcher.find());

            optMatcher.appendTail(buff);
            ret = buff.toString();
        }

        if (log.isDebugEnabled()) {
            log.debug("Return=" + ret);
        }
        return ret;
    }

    /**
     * Fixed parameters with the given parameters. The tag
     * names (xxx) must be composed of word characters ([a-zA-Z_0-9]) and
     * case-sensitive. If the value for a certain tag is found and not null, the
     * caught tag will be put with the value's text representation;
     * otherwise the tag name will be removed from parameters
     *
     * @param tplTxt
     *            the original template text
     * @param param
     *            parameters
     * @return fixed parameters List
     */
    public static List<String> fixParamForCompileSql(String tplTxt, Map<String, ?> param) {
        if (log.isDebugEnabled()) {
            log.debug("Tpl. Text=" + tplTxt + ", Param=" + param);
        }

        if (param == null) {
            param = EMPTY_PARAM;
        }

        List<String> fixedList = new ArrayList<String>();
        String ret = tplTxt;
        Matcher matcher = PATTERN_OPT_TAG.matcher(tplTxt);
        if (matcher.find()) {
            // At least one template tag has been found.
            StringBuffer buff = new StringBuffer(tplTxt.length());

            do {
                // Name of the tag caught here
                String tagName = matcher.group(1);
                // Value set in the parameters for the tag and its text
                Object tagValue = param.get(tagName);
                if (tagValue == null) {
                    matcher.appendReplacement(buff, "");
                    continue;
                } else {
                    fixedList.add(tagName);
                }
            } while (matcher.find());
        }

        if (log.isDebugEnabled()) {
            log.debug("Return=" + ret);
        }
        return fixedList;
    }

    /**
     * Removes the optional fragment (surrounded with '[]') in the given
     * template text if the specified condition is matched.
     *
     * @param tplTxt the original template text
     * @param param  parameters
     * @return the text with all unnecessary optinal fragment removed
     * @see #removeOptionalFrag(String, java.util.Map, boolean,
     *      boolean)
     */
    public static String removeOptionalFrag(String tplTxt, Map<String, ?> param) {
        return removeOptionalFrag(tplTxt, param, true, true);
    }
}
